import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { db } from '../config/database';
import logger from '../utils/logger';

// 类型定义
type User = {
  id: string;
  email: string;
  username?: string | null;
  avatar?: string | null;
  password?: string | null;
  emailVerified: boolean;
  isActive: boolean;
  lastLoginAt?: Date | null;
  loginAttempts: number;
  lockedUntil?: Date | null;
  plan: 'FREE' | 'PRO' | 'BUSINESS';
  credits: any;
  createdAt: Date;
  updatedAt: Date;
};


// 认证相关的类型定义
interface LoginCredentials {
  email: string;
  password: string;
}

interface RegisterData {
  email: string;
  username?: string;
  password: string;
}

interface AuthTokens {
  accessToken: string;
  refreshToken: string;
}

interface UserSessionData {
  userId: string;
  email: string;
  username?: string;
  plan: string;
  credits: number;
  isActive: boolean;
  emailVerified: boolean;
}

class AuthService {
  private readonly saltRounds = 12;
  private readonly maxLoginAttempts = 5;
  private readonly lockoutDuration = 15 * 60 * 1000; // 15分钟
  private readonly jwtSecret = process.env.JWT_ACCESS_SECRET || 'your-secret-key';
  private readonly jwtRefreshSecret = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret';
  private readonly accessTokenExpiry = process.env.JWT_ACCESS_EXPIRY || '15m';
  private readonly refreshTokenExpiry = process.env.JWT_REFRESH_EXPIRY || '7d';

  /**
   * 用户注册
   */
  async register(registerData: RegisterData): Promise<{ user: Partial<User>; tokens: AuthTokens }> {
    const { email, username, password } = registerData;

    // 检查邮箱是否已存在
    const existingUser = await this.findUserByEmail(email);
    if (existingUser) {
      throw new Error('该邮箱已被注册');
    }

    // 检查用户名是否已存在
    if (username) {
      const existingUsername = await this.findUserByUsername(username);
      if (existingUsername) {
        throw new Error('该用户名已被使用');
      }
    }

    // 密码加密
    const hashedPassword = await this.hashPassword(password);

    // 创建用户
    const prisma = await db.prisma();
    const newUser = await prisma.user.create({
      data: {
        email,
        username,
        password: hashedPassword,
        emailVerified: false,
        isActive: true,
        plan: 'FREE',
        credits: 10.0, // 新用户送10积分
      },
    });

    // 创建邮箱认证账户记录
    await prisma.account.create({
      data: {
        userId: newUser.id,
        provider: 'EMAIL',
        providerAccountId: newUser.id,
        providerEmail: email,
        providerUsername: username,
      },
    });

    // 生成 JWT Token
    const tokens = await this.generateTokens(newUser);

    // 存储会话信息到 Redis
    await this.storeUserSession(newUser, tokens.refreshToken);

    logger.info(`新用户注册成功: ${email}`);

    return {
      user: this.sanitizeUser(newUser),
      tokens,
    };
  }

  /**
   * 用户登录
   */
  async login(credentials: LoginCredentials): Promise<{ user: Partial<User>; tokens: AuthTokens }> {
    const { email, password } = credentials;

    // 查找用户
    const user = await this.findUserByEmail(email);
    if (!user) {
      throw new Error('邮箱或密码错误');
    }

    // 检查账户状态
    await this.checkAccountStatus(user);

    // 验证密码
    const isPasswordValid = await this.verifyPassword(password, user.password || '');
    if (!isPasswordValid) {
      // 记录登录失败
      await this.recordFailedLogin(user.id);
      throw new Error('邮箱或密码错误');
    }

    // 重置登录失败计数
    await this.resetLoginAttempts(user.id);

    // 更新最后登录时间
    await this.updateLastLogin(user.id);

    // 生成 JWT Token
    const tokens = await this.generateTokens(user);

    // 存储会话信息到 Redis
    await this.storeUserSession(user, tokens.refreshToken);

    logger.info(`用户登录成功: ${email}`);

    return {
      user: this.sanitizeUser(user),
      tokens,
    };
  }

  /**
   * 刷新令牌
   */
  async refreshToken(refreshToken: string): Promise<AuthTokens> {
    try {
      // 验证刷新令牌
      const decoded = jwt.verify(refreshToken, this.jwtRefreshSecret) as any;

      // 查找用户
      const user = await this.findUserById(decoded.userId);
      if (!user || !user.isActive) {
        throw new Error('用户不存在或已被禁用');
      }

      // 检查 Redis 中的会话
      const redis = await db.redis();
      const sessionData = await redis.get(`session:user:${user.id}`);
      if (!sessionData) {
        throw new Error('会话已过期');
      }

      const session = JSON.parse(sessionData);
      if (session.refreshToken !== refreshToken) {
        throw new Error('刷新令牌无效');
      }

      // 生成新的令牌对
      const newTokens = await this.generateTokens(user);

      // 更新会话信息
      await this.storeUserSession(user, newTokens.refreshToken);

      return newTokens;
    } catch (error) {
      logger.error('刷新令牌失败:', error);
      throw new Error('刷新令牌失败');
    }
  }

  /**
   * 用户登出
   */
  async logout(userId: string, accessToken: string): Promise<void> {
    try {
      const redis = await db.redis();

      // 删除会话信息
      await redis.del(`session:user:${userId}`);

      // 将访问令牌加入黑名单
      const decoded = jwt.decode(accessToken) as any;
      if (decoded && decoded.exp) {
        const ttl = decoded.exp - Math.floor(Date.now() / 1000);
        if (ttl > 0) {
          await redis.setex(`blacklist:token:${accessToken}`, ttl, '1');
        }
      }

      logger.info(`用户登出: ${userId}`);
    } catch (error) {
      logger.error('登出失败:', error);
      throw new Error('登出失败');
    }
  }

  /**
   * 修改密码
   */
  async changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void> {
    const user = await this.findUserById(userId);
    if (!user || !user.password) {
      throw new Error('用户不存在或未设置密码');
    }

    // 验证旧密码
    const isOldPasswordValid = await this.verifyPassword(oldPassword, user.password);
    if (!isOldPasswordValid) {
      throw new Error('当前密码错误');
    }

    // 加密新密码
    const hashedNewPassword = await this.hashPassword(newPassword);

    // 更新密码
    const prisma = await db.prisma();
    await prisma.user.update({
      where: { id: userId },
      data: { password: hashedNewPassword },
    });

    logger.info(`用户修改密码: ${userId}`);
  }

  /**
   * 重置密码
   */
  async resetPassword(email: string, newPassword: string, resetCode: string): Promise<void> {
    // 验证重置验证码
    const redis = await db.redis();
    const storedCode = await redis.get(`reset:${email}`);

    if (!storedCode || storedCode !== resetCode) {
      throw new Error('验证码无效或已过期');
    }

    // 查找用户
    const user = await this.findUserByEmail(email);
    if (!user) {
      throw new Error('用户不存在');
    }

    // 加密新密码
    const hashedPassword = await this.hashPassword(newPassword);

    // 更新密码
    const prisma = await db.prisma();
    await prisma.user.update({
      where: { id: user.id },
      data: {
        password: hashedPassword,
        loginAttempts: 0,
        lockedUntil: null,
      },
    });

    // 删除已使用的验证码
    await redis.del(`reset:${email}`);

    logger.info(`用户重置密码成功: ${email}`);
  }

  /**
   * 发送密码重置邮件
   */
  async sendPasswordResetEmail(email: string): Promise<void> {
    const user = await this.findUserByEmail(email);
    if (!user) {
      throw new Error('该邮箱未注册');
    }

    // 生成重置验证码
    const resetCode = this.generateResetCode();

    // 存储到 Redis，15分钟过期
    const redis = await db.redis();
    await redis.setex(`reset:${email}`, 15 * 60, resetCode);

    // 这里应该发送邮件，暂时只记录日志
    logger.info(`密码重置验证码已生成: ${email}, 验证码: ${resetCode}`);

    // TODO: 实际项目中这里应该调用邮件服务
    // await emailService.sendPasswordResetEmail(email, resetCode);
  }


  private async hashPassword(password: string): Promise<string> {
    return bcrypt.hash(password, this.saltRounds);
  }

  private async verifyPassword(password: string, hashedPassword: string): Promise<boolean> {
    return bcrypt.compare(password, hashedPassword);
  }

  async generateTokens(user: User): Promise<AuthTokens> {
    const payload = {
      userId: user.id,
      email: user.email,
      username: user.username || undefined,
      plan: user.plan,
    };

    const accessToken = jwt.sign(payload, this.jwtSecret, {
      expiresIn: this.accessTokenExpiry,
    } as jwt.SignOptions);

    const refreshToken = jwt.sign(payload, this.jwtRefreshSecret, {
      expiresIn: this.refreshTokenExpiry,
    } as jwt.SignOptions);

    return { accessToken, refreshToken };
  }

  async storeUserSession(user: User, refreshToken: string): Promise<void> {
    const sessionData: UserSessionData = {
      userId: user.id,
      email: user.email,
      username: user.username || undefined,
      plan: user.plan,
      credits: Number(user.credits),
      isActive: user.isActive,
      emailVerified: user.emailVerified,
    };

    const redis = await db.redis();
    const sessionKey = `session:user:${user.id}`;

    // 存储会话，7天过期
    await redis.setex(sessionKey, 7 * 24 * 60 * 60, JSON.stringify({
      ...sessionData,
      refreshToken,
      loginTime: new Date().toISOString(),
    }));
  }

  private async findUserByEmail(email: string): Promise<User | null> {
    const prisma = await db.prisma();
    return prisma.user.findUnique({
      where: { email },
    });
  }

  private async findUserByUsername(username: string): Promise<User | null> {
    const prisma = await db.prisma();
    return prisma.user.findUnique({
      where: { username },
    });
  }

  private async findUserById(id: string): Promise<User | null> {
    const prisma = await db.prisma();
    return prisma.user.findUnique({
      where: { id },
    });
  }

  private async checkAccountStatus(user: User): Promise<void> {
    // 检查账户是否激活
    if (!user.isActive) {
      throw new Error('账户已被禁用');
    }

    // 检查账户是否被锁定
    if (user.lockedUntil && user.lockedUntil > new Date()) {
      const unlockTime = user.lockedUntil.toLocaleString();
      throw new Error(`账户已被锁定，解锁时间: ${unlockTime}`);
    }

    // 检查是否需要解锁（锁定时间已过）
    if (user.lockedUntil && user.lockedUntil <= new Date()) {
      await this.resetLoginAttempts(user.id);
    }
  }

  private async recordFailedLogin(userId: string): Promise<void> {
    const prisma = await db.prisma();
    const user = await prisma.user.findUnique({ where: { id: userId } });

    if (!user) return;

    const newAttempts = user.loginAttempts + 1;
    const updateData: any = { loginAttempts: newAttempts };

    // 如果达到最大尝试次数，锁定账户
    if (newAttempts >= this.maxLoginAttempts) {
      updateData.lockedUntil = new Date(Date.now() + this.lockoutDuration);
      logger.warn(`账户因多次登录失败被锁定: ${user.email}`);
    }

    await prisma.user.update({
      where: { id: userId },
      data: updateData,
    });
  }

  private async resetLoginAttempts(userId: string): Promise<void> {
    const prisma = await db.prisma();
    await prisma.user.update({
      where: { id: userId },
      data: {
        loginAttempts: 0,
        lockedUntil: null,
      },
    });
  }

  private async updateLastLogin(userId: string): Promise<void> {
    const prisma = await db.prisma();
    await prisma.user.update({
      where: { id: userId },
      data: { lastLoginAt: new Date() },
    });
  }

  private sanitizeUser(user: User): Partial<User> {
    const { password, loginAttempts, lockedUntil, ...safeUser } = user;
    return safeUser;
  }

  private generateResetCode(): string {

    const chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ0';
    let code = '';
    for (let i = 0; i < 6; i++) {
      code += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return code;
  }

  /**
   * 处理用户登录后的操作（生成token、存储会话等）
   */
  async processUserLogin(user: User): Promise<{ user: Partial<User>; tokens: AuthTokens }> {
    // 检查账户状态
    await this.checkAccountStatus(user);

    // 生成JWT tokens
    const tokens = await this.generateTokens(user);

    // 存储用户会话
    await this.storeUserSession(user, tokens.refreshToken);

    // 更新最后登录时间
    await this.updateLastLogin(user.id);

    // 重置登录尝试次数
    await this.resetLoginAttempts(user.id);

    return {
      user: this.sanitizeUser(user),
      tokens
    };
  }


}

export default new AuthService();
